package com.my.ycv51.single;

import static com.cloudwalk.android.CWDefine.CWLDEnumCamType.CW_CAMERA_OPEN_ALL;
import static com.cloudwalk.android.CWDefine.CWLDEnumCamType.CW_CAMERA_OPEN_VIS;
import static com.cloudwalk.android.CWDefine.CW_SDK_SUCCESS;

import android.app.Activity;
import android.os.Handler;
import android.os.HandlerThread;

import com.cloudwalk.android.CWCameraDataCallback;
import com.cloudwalk.android.CWDefine;
import com.cloudwalk.android.CWDetectCallback;
import com.cloudwalk.android.CWFaceRect;
import com.cloudwalk.android.CWFeature;
import com.cloudwalk.android.CWLDImage;
import com.cloudwalk.android.CWPathInfo;
import com.cloudwalk.android.CWRecogInfo;
import com.cloudwalk.android.CWRet;
import com.my.ycv51.constant.FaceConstant;
import com.my.ycv51.event.SimpleEvent;
import com.my.ycv51.util.Change;
import com.my.ycv51.util.LogUtil;
import com.my.ycv51.ycinit.config.ErrorCode;
import com.my.ycv51.ycinit.impl.CWLiveEngine;
import com.my.ycv51.ycinit.mgr.CwStoreMgr;
import com.my.ycv51.ycinit.opengl.CWGLDisplay;
import com.my.ycv51.ycinit.opengl.GLFrameSurface;

import org.greenrobot.eventbus.EventBus;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 人脸识别单例
 */
public class SingleFace {

    private SingleFace() {
    }

    private String TAG = "【SingleFace】";

    private Activity activity;

    private Handler mWorkHandler;
    private HandlerThread mWorkThread;

    /**
     * 云从视频引擎
     */
    private CWLiveEngine mEngine;

    /**
     * 人脸识别控件
     */
    private GLFrameSurface surfaceView;

    /**
     * 云从引擎日志（打印用）
     */
    private final CWLiveEngine.OnEngineInterfaceCallListener mOnEngineInterfaceCallListener = (String msg) -> refreshDebugApiView(msg);

    /**
     * 开启相机参数
     */
    private CwStoreMgr.CameraOpenEntry cameraOpenEntry;

    /**
     * 人脸识别模型路径
     */
    private static final String DEFAULT_MODEL_PATH = "/data/local/tmp/assets/";

    /**
     * 双目摄像头开启的种类
     */
    private CWDefine.CWLDEnumCamType camOpenType = CW_CAMERA_OPEN_ALL;

    /**
     * 相机是否初始化了
     */
    private boolean mHasInitCamera = false;

    /**
     * 流是否开启了
     */
    private final AtomicBoolean isStreamOn = new AtomicBoolean(false);

    /**
     * 是否是第一帧
     */
    private final AtomicBoolean isFirstFrame = new AtomicBoolean(true);

    private static final int DEFAULT_CAMERA_WIDTH = 640;

    private static final int DEFAULT_CAMERA_HEIGHT = 480;

    private static final int DEFAULT_CAMERA_FPS = 30;

    private static final String DEFAULT_RGB_PID = "c002";

    private static final String DEFAULT_IR_PID = "b002";

    private static final String DEFAULT_RGB_VID = "0c45";

    private static final String DEFAULT_IR_VID = "0c45";

    /**
     * 云从视频显示参数设置
     */
    private CWGLDisplay mVisGLDisplay;

    /**
     * 是否活检配置完成
     * Carry out live detect,when mLiveType is zero,execute once,when that is one ,execute always.
     */
    private volatile boolean isLiveDetectSeting = false;

    //  新加人脸特征获取 mDisplayImage.setImage
    private final CWLDImage mDisplayImage = new CWLDImage();
    private final CWLDImage mLiveDetectImage = new CWLDImage();
    private final Object lockLiveDetect = new Object();

    private final CWFaceRect mLiveDetectRect = new CWFaceRect();

    /**
     * 人脸特征
     */
    private String userFace = null;

    /**
     * 是注册还是验证 0 检测,1录入,默认为检测
     */

    //    private int codeCheckOrRegist = 0;
    private final AtomicInteger codeCheckOrRegist = new AtomicInteger(0);

    public static SingleFace getInstance() {
        return SingleFaceHolder.instance;
    }

    private static class SingleFaceHolder {
        private static final SingleFace instance = new SingleFace();
    }

    /**
     * 业务逻辑
     * 1.初始化工作，传递activity和surfaceView
     * 2.传递控件
     * 3.开始人脸识别
     */

    /**
     * 初始化云从相机
     *
     * @param activity
     * @param surfaceView
     * @param codeCheckOrRegist 0检测，1.录入
     * @param userFace
     */
    public void initCWCamear(Activity activity, GLFrameSurface surfaceView, int codeCheckOrRegist, String userFace) {

        if (null == activity || null == surfaceView) {
            LogUtil.e(TAG + "activity or surfaceView is null");
            return;
        }

        if (codeCheckOrRegist == FaceConstant.CODE_CHECK_FACE) {
            if (null == userFace || userFace.isEmpty()) {
                LogUtil.e(TAG + "userFace=null or  userFace.isEmpty 检测模式没有传入人脸特征信息");
                return;
            }
        }

        LogUtil.e(TAG + "模式codeCheckOrRegist:" + codeCheckOrRegist);

        this.surfaceView = surfaceView;
        this.activity = activity;
        this.codeCheckOrRegist.set(codeCheckOrRegist);
        this.userFace = userFace;
        mVisGLDisplay = new CWGLDisplay();

        if (mWorkHandler == null) { // 第一次初始化
            initWorkHandlerThread();
            mEngine = CWLiveEngine.getInstance();

            loadAllConfig();

            mWorkHandler.post(this::initSDK);
        } else {
            startStream();
        }
    }


    /**
     * 关闭检测
     */
    public void close() {
        this.activity = null;
        this.surfaceView = null;
        mVisGLDisplay = null;

        stopStream();
    }

    /**
     * 初始化工作线程
     */
    private void initWorkHandlerThread() {
        mWorkThread = new HandlerThread("RegisterWorkThread");
        mWorkThread.start();
        mWorkHandler = new Handler(mWorkThread.getLooper());
    }

    /**
     * 加载所有配置
     */
    private void loadAllConfig() {
        mEngine.addEngineInterfaceCallListener(mOnEngineInterfaceCallListener);
        cameraOpenEntry = new CwStoreMgr.CameraOpenEntry(1,
                DEFAULT_RGB_PID, DEFAULT_IR_PID, DEFAULT_RGB_VID, DEFAULT_IR_VID,
                true, true);
    }

    /**
     * 日志
     *
     * @param s
     */
    private void refreshDebugApiView(String s) {
        LogUtil.e(TAG + s);
    }

    /**
     * 初始化云从相机
     */
    private void initSDK() {
//        // 停止检测
//        mEngine.stopDetect();
//        closeCamera();
//        mEngine.unInit();
        CWPathInfo cwPathInfo = new CWPathInfo();
        cwPathInfo.setConfigurePath(DEFAULT_MODEL_PATH);
        cwPathInfo.setLicensePath(DEFAULT_MODEL_PATH);
        {
            //初始化SDK
            CWRet<Long> cwRet = mEngine.init(activity.getApplicationContext(), cwPathInfo, CWDefine.CWLDEnumLiveMode.CW_LIVEMODE_AUTO, cameraOpenEntry.getOpenMode());
            if (CW_SDK_SUCCESS != cwRet.errorCode || cwRet.obj == null) {
                callBack(FaceConstant.CODE_FAIL, "SDK初始化失败 ErrorCode: " + cwRet.errorCode);
                return;
            }
            mEngine.setEventCallback(mCWLiveDetectCallback, null);
        }

        //打开相机
        CWRet cwRet = mEngine.openCamera(camOpenType);
        if (CW_SDK_SUCCESS != cwRet.errorCode) {
            callBack(FaceConstant.CODE_FAIL, ("初始化失败 ErrorCode: " + cwRet.errorCode));
            refreshDebugApiView("初始化失败");
            return;
        }

        mHasInitCamera = true;
        setCameraCommonParameters();

        LogUtil.i(TAG + "SDK初始化完毕" + cwRet.errorCode);

        if (!checkCWRetSucc(cwRet)) {
            LogUtil.e(TAG + "检测SDK初始化失败");
        } else {
            LogUtil.i(TAG + "检测SDK初始化成功");

            processStreamOnOff(); // 开启流
        }
    }

    private boolean checkCWRetSucc(CWRet cwRet) {
        return cwRet.errorCode == CW_SDK_SUCCESS;
    }

    /**
     * Process stream on and  off
     */
    private void processStreamOnOff() {
        mWorkHandler.post(() -> {
            if (!mHasInitCamera) {
                callBack(FaceConstant.CODE_FAIL, "还未初始化相机");
                return;
            }
            if (!isStreamOn.get()) {
                startStream();
            } else {
                stopStream();
                if (surfaceView != null && surfaceView.getCurrentRenderer() != null) {
                    try {
                        surfaceView.getCurrentRenderer().clear();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }


    /**
     * camera stream on.
     */

    private void startStream() {
        refreshDebugApiView("打开模组");
        isFirstFrame.set(true);
        CWRet ret = mEngine.cameraStreamOn(camOpenType, true);
        if (ret.errorCode != CWDefine.CW_SDK_SUCCESS) {
            callBack(FaceConstant.CODE_FAIL, "模组出流打开失败,Error: " + ret.errorCode);
        }
        isStreamOn.set(true);
    }


    /**
     * 设置相机公共参数
     */
    private void setCameraCommonParameters() {
        if (!mEngine.isInitialized() || !mHasInitCamera) {
            refreshDebugApiView("setCameraCommonParameters fail: camera not init");
            return;
        }
        CWRet cwRet = mEngine.setCameraRotateAndMirror(camOpenType, getCameraAngle(), getCameraMirror());
        if (!checkCWRetSucc(cwRet)) {
            callBack(FaceConstant.CODE_FAIL, "设置相机旋转镜像失败");

            refreshDebugApiView("setCameraRotateAndMirror " + cwRet.errorCode);
            return;
        }
        cwRet = mEngine.setCameraStreamFormat(camOpenType,
                CWDefine.CWEnumCameraStreamFormat.CW_CAMERA_STREAM_FORMAT_MJPEG, DEFAULT_CAMERA_WIDTH, DEFAULT_CAMERA_HEIGHT, DEFAULT_CAMERA_FPS);
        if (!checkCWRetSucc(cwRet)) {
            callBack(FaceConstant.CODE_FAIL, "设置相机出流参数失败");
            refreshDebugApiView("setCameraStreamFormat " + cwRet.errorCode);
            return;
        }
        cwRet = mEngine.setCameraDataCallback(camOpenType == CW_CAMERA_OPEN_ALL ? CW_CAMERA_OPEN_VIS : camOpenType,
                mCameraDataCallback,
                CWDefine.CWEnumCameraDataFormat.CW_CAMERA_FRAME_FORMAT_RGB888, true, null);
        if (!checkCWRetSucc(cwRet)) {
            callBack(FaceConstant.CODE_FAIL, "设置相机数据回调失败");

            refreshDebugApiView("setCameraDataCallback " + cwRet.errorCode);
            return;
        }
    }

    /**
     * 相机的回调
     */
    private final CWCameraDataCallback mCameraDataCallback = (cameraImage, cameraDevice, userParam) -> {

        if (!isStreamOn.get()) {
            LogUtil.i(TAG + "流未启动");
            callBack(FaceConstant.CODE_FAIL, "流未启动");
            return;
        }

        if (isFirstFrame.get()) { //第一帧
            isFirstFrame.set(false);
            LogUtil.i(TAG + "第一帧");
            processLiveDetect(); // 执行活检
        }

        if ((surfaceView != null) && (mVisGLDisplay != null)) {
            int renderDataType = 1;
            final CWDefine.CWEnumCameraDataFormat format = CWDefine.CWEnumCameraDataFormat.values()[cameraImage.getFormat()];
            switch (format) {
                case CW_CAMERA_FRAME_FORMAT_YUV420: {
                    renderDataType = 0;
                    break;
                }
                case CW_CAMERA_FRAME_FORMAT_RGB888: {
                    renderDataType = 1;
                    break;
                }
                case CW_CAMERA_FRAME_FORMAT_GRAY8: {
                    renderDataType = 2;
                    break;
                }
                case CW_CAMERA_FRAME_FORMAT_GRAY16: {
                    renderDataType = 3;
                    break;
                }
            }

            if (null == mVisGLDisplay) {
                LogUtil.e("mVisGLDisplay==null");
                callBack(FaceConstant.CODE_FAIL, "mVisGLDisplay NULL");
            } else {
                mVisGLDisplay.render(surfaceView,
                        cameraImage.getAngle(),
                        cameraImage.getMirror() == 1,
                        cameraImage.getData(),
                        cameraImage.getWidth(),
                        cameraImage.getHeight(), renderDataType);
            }

        }
    };

    /**
     * 活检处理
     */
    private void processLiveDetect() {
        LogUtil.i(TAG + "处理活检");
        if (isLiveDetectSeting) {
            return;
        }
        mWorkHandler.post(() -> {
            if (isLiveDetectSeting) {
                return;
            }
            isLiveDetectSeting = true;
            if (!mEngine.isInitialized() || !mHasInitCamera) {
                callBack(FaceConstant.CODE_MESSAGE, "不满足活检条件");
                isLiveDetectSeting = false;
                return;
            }
            CWRet<CWDefine.CWEnumCameraStatus> cwRet = mEngine.getCameraStatus(camOpenType == CW_CAMERA_OPEN_ALL ? CW_CAMERA_OPEN_VIS : camOpenType);
            if (!checkCWRetSucc(cwRet) || cwRet.obj != CWDefine.CWEnumCameraStatus.CW_CAMERA_STATUS_STREAMON) {
                callBack(FaceConstant.CODE_MESSAGE, "请先开启模组");
                isLiveDetectSeting = false;
                return;
            }

            singleDetect();

            isLiveDetectSeting = false;
        });
    }


    /**
     * 单一活检
     */
    private void singleDetect() {

        if (!isStreamOn.get()) { // 流未开启
            LogUtil.e(TAG + "stream is close");
            return;
        }

        LogUtil.i(TAG + "下发活检");
        if (!mEngine.isInitialized() || !mHasInitCamera) {
            LogUtil.e(TAG + "初始化null");
            return;
        }
        // 下发活检关键代码
        CWRet cwRet = mEngine.startDetect(CWDefine.CWEnumDetectType.CW_DECTYPE_LIVENESS, 0);

        if (!checkCWRetSucc(cwRet)) { // 活检异常提示
            if (75003007 == cwRet.errorCode) {
                LogUtil.i(TAG + "正在活检中");
            } else { // 活检异常
                callBack(FaceConstant.CODE_MESSAGE, ("下发活检异常 Error: " + cwRet.errorCode));
                LogUtil.e(TAG + "下发活检异常 Error: " + cwRet.errorCode);
            }
        }
    }

    /**
     * 相机预览角度
     *
     * @return
     */
    private CWDefine.CWEnumCameraDataAngle getCameraAngle() {
        return CWDefine.CWEnumCameraDataAngle.valueOf(0);
    }

    /**
     * 是否镜像 0不开启镜像，1开启镜像
     *
     * @return
     */
    private CWDefine.CWEnumCameraDataMirror getCameraMirror() {
        return CWDefine.CWEnumCameraDataMirror.values()[1];
    }

    /**
     * Live detect callback.
     */
    private CWDetectCallback mCWLiveDetectCallback = (errorCode, timestamp, cwLiveImage, cwDetectorCallbackData, userParam) -> {
        LogUtil.i(TAG + "mCWLiveDetectCallback:" + errorCode);

        if (cwDetectorCallbackData == null) {
            LogUtil.e(TAG + "cwDetectorCallbackData null");
            return;
        }
        switch (CWDefine.CWEventType.values()[cwDetectorCallbackData.getDetectEventType()]) {
            case CW_EVENT_FACE_DETECT: {
                singleDetect();
                break;
            }
            case CW_EVENT_LIVE_DETECT: {
                LogUtil.e(TAG + "活体");
                if (errorCode == ErrorCode.CW_FACE_LIV_IS_LIVE) { // 活体
                    synchronized (cwLiveImage) {
                        if (codeCheckOrRegist.get() == FaceConstant.CODE_CHECK_FACE) { // 检测人脸
                            mLiveDetectImage.setImage(new CWLDImage(cwLiveImage.getData(),
                                    cwLiveImage.getWidth(),
                                    cwLiveImage.getHeight(),
                                    CWDefine.CWEnumCameraDataFormat.values()[cwLiveImage.getFormat()],
                                    false), false);

                            mDisplayImage.setImage(mLiveDetectImage);

                            //  需要对比人脸
                            synchronized (lockLiveDetect) {
                                mLiveDetectRect.setFaceRect(cwDetectorCallbackData.getLiveDetectData().getArrFaceInformation()[0].getRect());
                                lockLiveDetect.notify();
                            }

                            float maxScore = contrastFace(Change.toBytes(userFace)); // 验证
                            LogUtil.e(TAG + "对比分数：" + maxScore);
                            if (maxScore >= 0.85) { // 验证成功
                                callBack(FaceConstant.CODE_SUCCESS, ("对比成功!"));
                            } else { // 验证失败
                                callBack(FaceConstant.CODE_MESSAGE, ("验证失败请重新验证"));
                                singleDetectDelay();
                            }
                        } else if (codeCheckOrRegist.get() == FaceConstant.CODE_REGIST_FACE) { // 录入人脸
                            mLiveDetectImage.setImage(new CWLDImage(cwLiveImage.getData(),
                                    cwLiveImage.getWidth(),
                                    cwLiveImage.getHeight(),
                                    CWDefine.CWEnumCameraDataFormat.values()[cwLiveImage.getFormat()],
                                    false), false);

                            mDisplayImage.setImage(mLiveDetectImage);
                            CWLDImage cameraImage = null;
                            synchronized (mDisplayImage) {
                                if (null != mDisplayImage.getData()) {
                                    cameraImage = mDisplayImage.clone();
                                }
                            }

                            CWRet<CWFeature> cwRet = mEngine.getFeature(cameraImage, null, false);
                            if (!checkCWRetSucc(cwRet)
                                    || cwRet.obj == null
                                    || cwRet.obj.getRecogInfo() == null
                                    || cwRet.obj.getRecogInfo().getFeature_data() == null
                                    || cwRet.obj.getFaceRect() == null
                                    || cwRet.obj.getFaceRect().size() <= 0
                                    || cwRet.obj.getFaceNum() <= 0) {
                                callBack(FaceConstant.CODE_FAIL, "提取人脸照特征失败");
                                return;
                            }

                            byte[] feature = cwRet.obj.getRecogInfo().getFeature_data();
                            String fea = Change.bytesToHex(feature);
                            LogUtil.e(TAG + "face:" + fea);
                            // 人脸特征采集成功
                            callBack(FaceConstant.CODE_SUCCESS, fea);

                        } else {
                            LogUtil.e(TAG + "未定义用途:" + codeCheckOrRegist);
                            callBack(FaceConstant.CODE_MESSAGE, "未定义用途:" + codeCheckOrRegist);
                        }

                    }


                } else { // 活检异常
                    int finalErrorCode = Long.valueOf(errorCode).intValue();

                    mWorkHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            showLiveResult(finalErrorCode);
                        }
                    });

                    singleDetectDelay();
                }
                break;
            }
        }
    };

    /**
     * 展示检测结果
     *
     * @param errorCode
     */
    private void showLiveResult(int errorCode) {
        switch (errorCode) {
            case ErrorCode.CW_FACE_LIV_IS_UNLIVE_ERR:
                callBack(FaceConstant.CODE_MESSAGE, ("非活体"));
                break;
            case ErrorCode.CW_FACE_LIV_DIST_TOO_FAR_ERR:
                callBack(FaceConstant.CODE_MESSAGE, ("请离近一点"));
                break;
            case ErrorCode.CW_FACE_LIV_DIST_TOO_CLOSE_ERR:
                callBack(FaceConstant.CODE_MESSAGE, ("请离远一点"));
                break;
            default:
                callBack(FaceConstant.CODE_MESSAGE, ("请正对摄像头,保持人脸清晰"));
                break;
        }
    }


    /**
     * 延时活检
     */
    private void singleDetectDelay() {
        mWorkHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                singleDetect();
            }
        }, 1000);
    }


    /**
     * 人脸特征长度
     */
    private int featureLength = 0;

    /**
     * 人脸对比
     */
    private float contrastFace(byte[] mLibFeatures) {
        CWLDImage mRecognizeCameraImage = mDisplayImage.clone();
        CWFaceRect mRecognizeFaceRect;

        mRecognizeFaceRect = mLiveDetectRect.clone();

        if (featureLength == 0) {
            featureLength = mEngine.getFeatureLength().obj;
        }

        float maxScore = getMaxRecognizeScore(mRecognizeCameraImage, mRecognizeFaceRect, mLibFeatures, 1);
        return maxScore;
    }

    /**
     * Get max recog score
     *
     * @param mCurrentImage
     * @param cwFaceRect
     * @return
     */
    private float getMaxRecognizeScore(CWLDImage mCurrentImage, CWFaceRect cwFaceRect, byte[] mLibFeatures, int mlibSize) {

        if (mCurrentImage == null || cwFaceRect == null) {
            return 0;
        }

        // 获取当前人脸图片
        CWRet<CWFeature> cwRet = mEngine.getFeature(mCurrentImage, cwFaceRect, false);
        if (!checkCWRetSucc(cwRet) || null == cwRet.obj) {
            LogUtil.e(TAG + "获取人脸特征失败 ErrorCode: " + cwRet.errorCode);
            return 0;
        }
        CWRecogInfo cwRecogInfo = cwRet.obj.getRecogInfo();
        //Get lib from disk
        if (mLibFeatures != null && mlibSize > 0) {
            //Compare
            CWRet<float[]> cwRet1 = mEngine.compareFeature(cwRecogInfo.getFeature_data(), mLibFeatures, mlibSize);
            if (!checkCWRetSucc(cwRet1) || cwRet1.obj == null) {
                LogUtil.e(TAG + "比对失败");
                return 0;
            }
            float[] mRecogScores = cwRet1.obj;
            //Get max score
            float maxScore = 0.0f;
            for (int i = 0; i < mRecogScores.length; i++) {
                if (mRecogScores[i] > maxScore) {
                    maxScore = mRecogScores[i];
                }
            }
            return maxScore;
        }
        return 0;
    }


    /**
     * 关闭相机
     */
    private void closeCamera() {
        stopStream();
        mEngine.closeCamera(camOpenType);
        mHasInitCamera = false;
        if (surfaceView != null && surfaceView.getCurrentRenderer() != null) {
            surfaceView.getCurrentRenderer().clear();
        }
    }

    /**
     * camera stream off.
     */
    private void stopStream() {
        refreshDebugApiView("模组出流关闭");
        mEngine.cameraStreamOn(camOpenType, false);
        isStreamOn.set(false);
    }

    /**
     * EventBus回调，弱引用，防止内存泄露
     *
     * @param code
     * @param msg
     */
    private void callBack(int code, String msg) {
        EventBus.getDefault().post(new SimpleEvent(code, msg));
    }
}
